// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_entity_terrain.h"
#include "mk_error.h"
#include "mk_engine.h"
#include "mk_render_device.h"
#include "mk_entity_terrain_region.h"
#include "mk_entity_model.h"
#include "mk_memory_block.h"
#include "mk_math.h"
#include "mk_terrain_height_brush.h"
#include "mk_terrain_layer_brush.h"
#include "mk_file_system.h"
#include "mk_camera.h"
#include "mk_render_window.h"
#include "mk_material.h"
#include "mk_mp_culling_mode.h"

namespace MK3DE
{
	const int GAUSS_MAP_SIZE = 13;

	CEntityTerrain::CEntityTerrain(CScene* pScene)
		: CEntity(pScene)
	{
		_width = 0;
		_height = 0;
		_material = NULL;
	}

	CEntityTerrain::~CEntityTerrain()
	{
		__Clean();
	}

	BOOL CEntityTerrain::LoadFromFile(const char* fileName)
	{
		BOOL ret = FALSE;
		int result = 1;
		TiXmlDocument* pXML = NULL;
		TiXmlElement* node = NULL;
		int regionWidth = 0;
		int regionHeight = 0;

		CMemoryBlock block;
		ret = GetEngine()->GetFileSystem()->ReadFile(fileName, &block);
		MK_PROCESS_ERROR(ret);

		pXML = new TiXmlDocument();
		MK_PROCESS_ERROR(pXML);

		pXML->SetCondenseWhiteSpace(false);
		pXML->Parse((const char*)block.GetBuffer());
		MK_PROCESS_ERROR(pXML->Error() == false);

		node = pXML->RootElement();
		MK_PROCESS_ERROR(node);

		result = strcmp(node->Value(), "Terrain");
		MK_PROCESS_ERROR(result == 0);

		result = node->QueryIntAttribute("RegionWidth", &regionWidth);
		MK_PROCESS_ERROR(result == TIXML_SUCCESS);

		result = node->QueryIntAttribute("RegionHeight", &regionHeight);
		MK_PROCESS_ERROR(result == TIXML_SUCCESS);

		ret = Create(regionWidth, regionHeight);
		MK_PROCESS_ERROR(ret);

		node = node->FirstChildElement("Region");
		while (node)
		{
			int w = 0;
			int h = 0;
			const char* shaderFile = NULL;
			const char* heightFile = NULL;
			CEntityTerrainRegion* tr = NULL;
			TiXmlElement* layerNode = NULL;

			result = node->QueryIntAttribute("w", &w);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = node->QueryIntAttribute("h", &h);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			shaderFile = node->Attribute("shader");
			MK_PROCESS_ERROR(shaderFile);

			heightFile = node->Attribute("heightMap");
			MK_PROCESS_ERROR(heightFile);

			ret = GetTerrainRegionByIndex(w, h, tr);
			MK_PROCESS_ERROR(ret);

			ret = tr->Create(w, h, shaderFile);
			MK_PROCESS_ERROR(ret);

			ret = tr->LoadHeightMap(heightFile);
			MK_PROCESS_ERROR(ret);

			layerNode = node->FirstChildElement("Layer");
			while (layerNode)
			{
				const char* texName = NULL;
				const char* texFile = NULL;
				int isLayer = 0;

				texName = layerNode->Attribute("texName");
				MK_PROCESS_ERROR(texName);

				texFile = layerNode->Attribute("texFile");
				MK_PROCESS_ERROR(texFile);

				result = layerNode->QueryIntAttribute("layer", &isLayer);
				MK_PROCESS_ERROR(result == TIXML_SUCCESS);

				ret = tr->AddTextureMapFromFile(texName, texFile, isLayer);
				MK_PROCESS_ERROR(ret);

				layerNode = layerNode->NextSiblingElement("Layer");
			}

			node = node->NextSiblingElement("Region");
		}

		LinkAllRegion();
		RecalculateNormal();
		UpdatePositionVertexBuffer();
		UpdateNormalVertexBuffer();
		UpdateTextureCoordVertexBuffer();

		_fileName = fileName;

		ret = TRUE;
Exit0:
		SAFE_DELETE(pXML);

		return ret;
	}

	BOOL CEntityTerrain::Create(int regionWidth, int regionHeight)
	{
		BOOL ret = FALSE;

		__Clean();

		for (int i = 0; i < regionWidth * regionHeight; ++i)
		{
			CEntityTerrainRegion* region = new CEntityTerrainRegion(_scene, this);
			MK_PROCESS_ERROR(region);

			_vecTerrainRegion.push_back(region);
		}

		_material = new CMaterial();
		MK_PROCESS_ERROR(_material);

		ret = _material->CreateAllProperties();
		MK_PROCESS_ERROR(ret);

		_material->GetCullingModeProperty()->SetCullingMode(CM_CCW);

		_width = regionWidth;
		_height = regionHeight;

		ret = TRUE;
Exit0:
		return ret;
	}

	void CEntityTerrain::_AttachSceneNode(	CSceneNode* pNode)
	{
		_sceneNode = pNode;
		std::vector<CEntityTerrainRegion*>::iterator i = _vecTerrainRegion.begin();
		while (i != _vecTerrainRegion.end())
		{
			CEntityTerrainRegion* region = *i;
			region->_AttachSceneNode(pNode);
			++i;
		}
	}

	void CEntityTerrain::ViewFrustumCulling(CCamera* camera, std::vector<CEntity*>& vecEntity)
	{
		std::vector<CEntityTerrainRegion*>::iterator i = _vecTerrainRegion.begin();
		while (i != _vecTerrainRegion.end())
		{
			CEntityTerrainRegion* region = *i;
			if (camera->TestFrustumAABB(region->GetWorldAABB()))
				vecEntity.push_back(region);

			++i;
		}
	}

	void CEntityTerrain::LinkAllRegion()
	{
		for (int x = 0; x < (int)_width; ++x)
			for (int y = 0; y < (int)_height; ++y)
			{
				CTerrainRegion* up = NULL;
				CTerrainRegion* right = NULL;
				CTerrainRegion* down = NULL;
				CTerrainRegion* left = NULL;

				if (y + 1 < (int)_height)
					up = ((CEntityTerrainRegion*)_vecTerrainRegion[(y + 1) * _width + x])->GetCoreTerrainRegion();
				if (x + 1 < (int)_height)
					right = ((CEntityTerrainRegion*)_vecTerrainRegion[y * _width + x + 1])->GetCoreTerrainRegion();
				if (y - 1 >= 0)
					down = ((CEntityTerrainRegion*)_vecTerrainRegion[(y - 1) * _width + x])->GetCoreTerrainRegion();
				if (x - 1 >= 0)
					left = ((CEntityTerrainRegion*)_vecTerrainRegion[y * _width + x - 1])->GetCoreTerrainRegion();

				CEntityTerrainRegion* thisRegion = (CEntityTerrainRegion*)_vecTerrainRegion[y * _width + x];
				thisRegion->GetCoreTerrainRegion()->AttachNeighboringRegion(up, right, down, left);
			}
	}

	BOOL CEntityTerrain::GetTerrainRegionByIndex(int regionX, int regionY, CEntityTerrainRegion*& region)
	{
		MK_PROCESS_ERROR(regionX >= 0);
		MK_PROCESS_ERROR(regionY >= 0);
		MK_PROCESS_ERROR(regionX < (int)_width);
		MK_PROCESS_ERROR(regionY < (int)_height);

		region = (CEntityTerrainRegion*)_vecTerrainRegion[regionY * _width + regionX];

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CEntityTerrain::GetTerrainRegionByPos(float x, float y, /* out */ CEntityTerrainRegion*& region)
	{
		int rx = (int)(x / TERRAIN_REGION_WIDTH_SIZE);
		int ry = (int)(y / TERRAIN_REGION_HEIGHT_SIZE);
		if (!GetTerrainRegionByIndex(rx, ry, region))
			return FALSE;
		return TRUE;
	}

	void CEntityTerrain::__Clean()
	{
		std::vector<CEntityTerrainRegion*>::iterator i = _vecTerrainRegion.begin();
		while (i != _vecTerrainRegion.end())
		{
			CEntityTerrainRegion* region = *i;
			region->_AttachSceneNode(NULL);
			SAFE_DELETE(region);
			++i;
		}
		_vecTerrainRegion.clear();

		SAFE_DELETE(_material);
	}

	BOOL CEntityTerrain::GetGroundHeight(/* out */ float& height, float x, float y)
	{
		CEntityTerrainRegion* region = NULL;
		int rx = (int)(x / TERRAIN_REGION_WIDTH_SIZE);
		int ry = (int)(y / TERRAIN_REGION_HEIGHT_SIZE);
		if (rx < 0 || ry < 0 || rx >= (int)_width || ry >= (int)_height)
			return FALSE;

		if (!GetTerrainRegionByIndex(rx, ry, region))
			return FALSE;

		if (!region->GetGroundHeight(height, x, y,
			x - TERRAIN_REGION_WIDTH_SIZE * rx, 
			y - TERRAIN_REGION_HEIGHT_SIZE * ry))
			return FALSE;

		return TRUE;

	}

	BOOL CEntityTerrain::Pick(/* out */ D3DXVECTOR3& intersect, D3DXVECTOR2& pos2D)
	{
		CCamera* cam = GetEngine()->GetCurrentCamera();
		CRenderWindow* rw = GetEngine()->GetCurrentRenderWindow();

		D3DXVECTOR3 origPos;
		D3DXVECTOR3 dir;
		D3DXVECTOR3 p;
		cam->GetPickRay(origPos, dir, pos2D, rw->GetWidth(), rw->GetHeight());
		p = origPos + dir;

		D3DXPLANE plane;
		D3DXPlaneFromPointNormal(&plane, &D3DXVECTOR3(0, -100, 0), &D3DXVECTOR3(0, 1.0f, 0));
		if (!D3DXPlaneIntersectLine(&intersect, &plane, &origPos, &p))
			return FALSE;

		D3DXVECTOR3 v = intersect - origPos;
		if (D3DXVec3Dot(&v, &dir) <= 0)
			return FALSE;

		if (!__GetIntersection(intersect, origPos, intersect))
			return FALSE;

		return TRUE;
	}

	BOOL CEntityTerrain::__GetIntersection(D3DXVECTOR3& intersect, D3DXVECTOR3& v1, D3DXVECTOR3& v2)
	{
		float Height[17];
		D3DXVECTOR3 PosLine[17];
		for (int i = 0; i <= 16; i++)
		{
			PosLine[i] = v1 + (v2 - v1) / 16.0F * (float)i;
			GetGroundHeight(Height[i], PosLine[i].x, PosLine[i].z);
		}

		for (int i = 0; i < 16; i++)
		{
			if (
				(Height[i] >= PosLine[i].y && Height[i+1] <= PosLine[i + 1].y) ||
				(Height[i] <= PosLine[i].y && Height[i+1] >= PosLine[i + 1].y)
				)
			{
				D3DXVECTOR3 V = PosLine[i+1] - PosLine[i];
				if (D3DXVec3LengthSq(&V) < 10000.0f)
				{
					intersect = (PosLine[i] + PosLine[i + 1]) * 0.5F;
					return TRUE;
				}
				else
				{
					return __GetIntersection(intersect, PosLine[i], PosLine[i + 1]);
				}
			}

		}

		return FALSE;
	}

	BOOL CEntityTerrain::PaintHeight(CTerrainHeightBrush* brush)
	{
		float x, y;
		brush->GetPosition(x, y);
		float halfSize = brush->GetSize() * 0.5f;
		float left = x - halfSize - TERRAIN_CELL_WIDTH * 2.0f;
		float right = x + halfSize + TERRAIN_CELL_WIDTH * 2.0f;
		float top = y - halfSize - TERRAIN_CELL_HEIGHT * 2.0f;
		float bottom = y + halfSize + TERRAIN_CELL_HEIGHT * 2.0f;

		std::set<CEntityTerrainRegion*> regionSet;
		float offset = TERRAIN_CELL_WIDTH;
		for (float xPos = left; xPos < right; xPos += offset)
		{
			for (float yPos = top; yPos < bottom; yPos += offset)
			{
				CEntityTerrainRegion* region = NULL;
				BOOL ret = GetTerrainRegionByPos(xPos, yPos, region);
				if (ret)
					regionSet.insert(region);
			}
		}

		std::set<CEntityTerrainRegion*>::iterator i = regionSet.begin();
		while (i != regionSet.end())
		{
			CEntityTerrainRegion* region = *i;
			region->PaintHeight(brush);

			++i;
		}

		i = regionSet.begin();
		while (i != regionSet.end())
		{
			CEntityTerrainRegion* region = *i;
			region->GetCoreTerrainRegion()->RecalculateNormal();

			++i;
		}

		i = regionSet.begin();
		while (i != regionSet.end())
		{
			CEntityTerrainRegion* region = *i;
			region->UpdateNormalVertexBuffer();

			++i;
		}

		return TRUE;
	}

	BOOL CEntityTerrain::PaintLayer(CTerrainLayerBrush* brush, const char* fileName)
	{
		float x, y;
		brush->GetPosition(x, y);
		float halfSize = brush->GetSize() * 0.5f;
		std::vector<CEntityTerrainRegion*> vecRegion;
		D3DXVECTOR2 posList[4] = {
			D3DXVECTOR2(x - halfSize, y - halfSize),
			D3DXVECTOR2(x - halfSize, y + halfSize),
			D3DXVECTOR2(x + halfSize, y - halfSize),
			D3DXVECTOR2(x + halfSize, y + halfSize),
		};

		for (int i = 0; i < 4; i++)
		{
			CEntityTerrainRegion* region = NULL;
			BOOL ret = GetTerrainRegionByPos(posList[i][0], posList[i][1], region);
			if (ret)
			{
				std::vector<CEntityTerrainRegion*>::iterator it = vecRegion.begin();
				while (it != vecRegion.end())
				{
					if (region == (*it))
						break;
					++it;
				}

				if (it == vecRegion.end())
					vecRegion.push_back(region);
			}
		}

		std::vector<CEntityTerrainRegion*>::iterator i = vecRegion.begin();
		while (i != vecRegion.end())
		{
			CEntityTerrainRegion* region = *i;
			region->PaintLayer(brush, fileName);
			++i;
		}

		return TRUE;
	}

	void CEntityTerrain::RecalculateNormal()
	{
		std::vector<CEntityTerrainRegion*>::iterator it = _vecTerrainRegion.begin();
		while (it != _vecTerrainRegion.end())
		{
			CEntityTerrainRegion* region = *it;
			region->GetCoreTerrainRegion()->RecalculateNormal();

			++it;
		}
	}

	BOOL CEntityTerrain::UpdatePositionVertexBuffer()
	{
		std::vector<CEntityTerrainRegion*>::iterator it = _vecTerrainRegion.begin();
		while (it != _vecTerrainRegion.end())
		{
			CEntityTerrainRegion* region = *it;
			region->UpdatePositionVertexBuffer();
			++it;
		}

		return TRUE;
	}

	BOOL CEntityTerrain::UpdateNormalVertexBuffer()
	{
		std::vector<CEntityTerrainRegion*>::iterator it = _vecTerrainRegion.begin();
		while (it != _vecTerrainRegion.end())
		{
			CEntityTerrainRegion* region = *it;
			region->UpdateNormalVertexBuffer();
			++it;
		}

		return TRUE;
	}

	BOOL CEntityTerrain::UpdateTextureCoordVertexBuffer()
	{
		std::vector<CEntityTerrainRegion*>::iterator it = _vecTerrainRegion.begin();
		while (it != _vecTerrainRegion.end())
		{
			CEntityTerrainRegion* region = *it;
			region->UpdateTextureCoordVertexBuffer();
			++it;
		}

		return TRUE;
	}

	//    void CEntityTerrain::__GenerateShadowCasterList(CSceneNode* node, std::vector<CSceneNode*>& list)
	//    {
	//        if (node->GetType() == SNT_MODEL)
	//            list.push_back(node);
	//
	//        DWORD count = node->GetChildCount();
	//        for (DWORD i = 0; i < count; i++)
	//        {
	//            CSceneNode* subNode = node->GetChildByIndex(i);
	//            __GenerateShadowCasterList(subNode, list);
	//        }
	//    }
	//
	//    BOOL CEntityTerrain::__SceneShadowCasterCollideWithRay(D3DXVECTOR3& pos, float x, float y, float z, D3DXVECTOR3& lightDir, std::vector<CSceneNode*> vecShadowCaster)
	//    {
	//        std::vector<CSceneNode*>::iterator i = vecShadowCaster.begin();
	//        while (i != vecShadowCaster.end())
	//        {
	//            CSceneNode* node = *i;
	//            if (node->GetType() == SNT_MODEL)
	//            {
	//                CEntityModel* model = (CEntityModel*)node;
	//                if (model->CollideWithRay(pos, x, y, z, lightDir))
	//                    return TRUE;
	//            }
	//
	//            ++i;
	//        }
	//
	//        return FALSE;
	//    }
	//
	//    BOOL CEntityTerrain::__ConstructLightViewProjMatrix(D3DXMATRIX& viewMatrix, D3DXMATRIX& projMatrix, CEntityTerrainRegion* region, D3DXVECTOR3& lightDir)
	//    {
	//        BOOL ret = FALSE;
	//
	//        D3DXVECTOR3 viewPos;
	//        D3DXVECTOR3 pos;
	//
	//        // use the first vertex as light-view position.
	//        ret = region->GetVertexPosition(pos, 0, 0);
	//        MK_PROCESS_ERROR(ret);
	//
	//        D3DXMatrixLookAtLH(&viewMatrix, &pos, &(pos + lightDir), &D3DXVECTOR3(0, 1, 0));
	//
	//        // in light-view's space, find the bound.
	//        float minX = MK_MAX_FLOAT;
	//        float minY = MK_MAX_FLOAT;
	//        float maxX = MK_MIN_FLOAT;
	//        float maxY = MK_MIN_FLOAT;
	//        float minZ = MK_MAX_FLOAT;
	//        float maxZ = MK_MIN_FLOAT;
	//
	//        for (int y = 0; y < TERRAIN_REGION_HEIGHT_VERTEX; ++y)
	//        {
	//            for (int x = 0; x < TERRAIN_REGION_WIDTH_VERTEX; ++x)
	//            {
	//                ret = region->GetVertexPosition(pos, x, y);
	//                MK_PROCESS_ERROR(ret);
	//
	//                D3DXVec3TransformCoord(&viewPos, &pos, &viewMatrix);
	//                minX = min(minX, viewPos.x);
	//                minY = min(minY, viewPos.y);
	//                minZ = min(minZ, viewPos.z);
	//
	//                maxX = max(maxX, viewPos.x);
	//                maxY = max(maxY, viewPos.y);
	//                maxZ = max(maxZ, viewPos.z);
	//            }
	//        }
	//
	//        ASSERT(minX < MK_MAX_FLOAT);
	//        ASSERT(minY < MK_MAX_FLOAT);
	//        ASSERT(minZ < MK_MAX_FLOAT);
	//        ASSERT(maxX > MK_MIN_FLOAT);
	//        ASSERT(maxY > MK_MIN_FLOAT);
	//        ASSERT(maxZ > MK_MIN_FLOAT);
	//
	//        // calculate light-view's position and the view-dimension.
	//        {
	//            float projWidth = maxX - minX;
	//            float projHeight = maxY - minY;
	//
	//            float centerX = (minX + maxX) * 0.5f;
	//            float centerY = (minY + maxY) * 0.5f;
	//            float centerZ = maxZ - 10000.0f;
	//
	//            D3DXMATRIX invViewMatrix;
	//            D3DXMatrixInverse(&invViewMatrix, NULL, &viewMatrix);
	//            D3DXVec3TransformCoord(&viewPos, &D3DXVECTOR3(centerX, centerY, centerZ), &invViewMatrix);
	//            D3DXMatrixLookAtLH(&viewMatrix, &viewPos, &(viewPos + lightDir), &D3DXVECTOR3(0, 1.0f, 0));
	//            D3DXMatrixOrthoLH(&projMatrix, projWidth, projHeight, 1.0f, 10001.0f);
	//        }
	//
	//        ret = TRUE;
	//Exit0:
	//        return ret;
	//    }
	//
	//    BOOL CEntityTerrain::GenerateLightMapShadowMapping(const char* mapName, DWORD regionX, DWORD regionY, DWORD mapSize, D3DXVECTOR3& lightDir, float blurSize, float shadowFactor)
	//    {
	//        BOOL ret = FALSE;
	//
	//        CTexture2D* shadowMap = NULL;
	//        float* src = NULL;
	//        BYTE* dst = NULL;
	//        DWORD srcPitch = 0;
	//        DWORD dstPitch = 0;
	//        char fileName[MAX_PATH];
	//        std::string rootPath = GetEngine()->GetFileSystem()->GetRoot();
	//
	//        std::vector<CSceneNode*> vecModel;
	//        __GenerateShadowCasterList(GetEngine()->GetSceneManager(), vecModel);
	//
	//        sprintf(fileName, "__shadow_map_%d_%d.dds", regionX, regionY);
	//        D3DXVec3Normalize(&lightDir, &lightDir);
	//        CEntityTerrainRegion* region = (CEntityTerrainRegion*)_vecTerrainRegion[regionY * _width + regionX];
	//        CResourceManager* resourceMgr = GetEngine()->GetResourceManager();
	//        CRenderDevice* renderDevice = GetEngine()->GetRenderDevice();
	//
	//        // we assume light-dir's Y is negative.
	//        if (lightDir.y >= 0)
	//            return FALSE;
	//
	//        // construct light's view-projection matrix.
	//        D3DXMATRIX viewMatrix, projMatrix;
	//        ret = __ConstructLightViewProjMatrix(viewMatrix, projMatrix, region, lightDir);
	//        MK_PROCESS_ERROR(ret);
	//
	//        // create shadow map.
	//        ret = resourceMgr->GetTexture2D(mapSize, mapSize, PF_R32F, 1, TU_RENDER_TARGET, shadowMap);
	//        MK_PROCESS_ERROR(ret);
	//
	//        // render shadow map.
	//        renderDevice->SetRenderTarget(shadowMap);
	//        renderDevice->Clear(TRUE, TRUE, TRUE, 0xFFFFFFFF, 1.0f, 0);
	//        RenderShadow(viewMatrix, projMatrix);
	//        for (int i = 0; i < (int)vecModel.size(); ++i)
	//            vecModel[i]->RenderShadow(viewMatrix, projMatrix);
	//        renderDevice->SetRenderTarget(NULL);
	//        shadowMap->SaveToFile((rootPath + fileName).c_str());
	//        SAFE_RELEASE(shadowMap);
	//        ret = resourceMgr->GetTexture2DFromFile(fileName, shadowMap);
	//        MK_PROCESS_ERROR(ret);
	//
	//        // make light map.
	//        {
	//            float fx = TERRAIN_REGION_WIDTH_SIZE / (float)mapSize;
	//            float fy = TERRAIN_REGION_HEIGHT_SIZE / (float)mapSize;
	//
	//            float start_x = (float)regionX * TERRAIN_REGION_WIDTH_SIZE;
	//            float start_y = (float)regionY * TERRAIN_REGION_HEIGHT_SIZE;
	//
	//            CTexture2D* lightMap = region->GetTextureMap(mapName, FALSE);
	//            MK_PROCESS_ERROR(lightMap);
	//
	//            ret = shadowMap->LockRect((void**)&src, &srcPitch, NULL);
	//            MK_PROCESS_ERROR(ret);
	//
	//            ret = lightMap->LockRect((void**)&dst, &dstPitch, NULL);
	//            MK_PROCESS_ERROR(ret);
	//
	//            for (DWORD map_y = 0; map_y < mapSize; map_y++)
	//            {
	//                BYTE* dstBuffer = dst + map_y * dstPitch;
	//                for (DWORD map_x = 0; map_x < mapSize; map_x++)
	//                {
	//                    float x = ((float)map_x + 0.5f) * fx + start_x;
	//                    float z = ((float)map_y + 0.5f) * fy + start_y;
	//
	//                    float y;
	//                    ret = GetGroundHeight(y, x, z);
	//                    MK_PROCESS_ERROR(ret);
	//
	//                    D3DXVECTOR3 pos;
	//                    ProjectPoint(pos, D3DXVECTOR3(x, y, z), mapSize, mapSize, viewMatrix, projMatrix);
	//
	//                    float shadowMapZ = *((float*)((BYTE*)src + (int)pos.y * srcPitch) + (int)pos.x);
	//                    if (pos.z > shadowMapZ + 0.001f)
	//                        dstBuffer[map_x] = (BYTE)(255.0f * shadowFactor);
	//                    else
	//                        dstBuffer[map_x] = 255;
	//                }
	//            }
	//
	//            __GaussianBlurH(dst, dstPitch, mapSize, blurSize);
	//            __GaussianBlurV(dst, dstPitch, mapSize, blurSize);
	//
	//            lightMap->Unlock();
	//        }
	//
	//        shadowMap->Unlock();
	//
	//        ret = TRUE;
	//Exit0:
	//        SAFE_RELEASE(shadowMap);
	//        return ret;
	//    }
	//
	//    void CEntityTerrain::__GaussianBlurH(BYTE* buffer, DWORD pitch, DWORD mapSize, float blurSize)
	//    {
	//        // make a copy.
	//        BYTE* cloneBuffer = new BYTE[mapSize * mapSize];
	//        for (DWORD y = 0; y < mapSize; ++y)
	//            for (DWORD x = 0; x < mapSize; ++x)
	//                cloneBuffer[mapSize * y + x] = *(buffer + y * pitch + x);
	//        
	//        // calculate weight and offset.
	//        float BlurWeights[GAUSS_MAP_SIZE];
	//        int PixelKernel[GAUSS_MAP_SIZE];
	//        int halfGaussMapSize = GAUSS_MAP_SIZE / 2;
	//        for (int i = 0; i < GAUSS_MAP_SIZE; ++i)
	//        {
	//            PixelKernel[i] = i - halfGaussMapSize;
	//            BlurWeights[i] = GaussianDistribution1((float)(i - halfGaussMapSize), blurSize);
	//        }
	//
	//        // blur the map.
	//        for (int y = 0; y < (int)mapSize; ++y)
	//        {
	//            for (int x = 0; x < (int)mapSize; ++x)
	//            {
	//                float value = 0;
	//
	//                for (int ox = 0; ox < GAUSS_MAP_SIZE; ox++)
	//                {
	//                    float weight = BlurWeights[ox];
	//                    int mx = x + PixelKernel[ox];
	//                    if (mx < 0) 
	//                        mx = 0;
	//
	//                    if (mx >= (int)mapSize) 
	//                        mx = mapSize - 1;
	//
	//                    value += weight * (float)(cloneBuffer[y * mapSize + mx]);
	//                }
	//                
	//                if (value < 0) 
	//                    value = 0;
	//
	//                if (value > 255.0f) 
	//                    value = 255.0f;
	//
	//                *(buffer + y * pitch + x) = (BYTE)value;
	//            }
	//        }
	//
	//        SAFE_DELETE_ARRAY(cloneBuffer);
	//    }
	//
	//    void CEntityTerrain::__GaussianBlurV(BYTE* buffer, DWORD pitch, DWORD mapSize, float blurSize)
	//    {
	//        // make a copy.
	//        BYTE* cloneBuffer = new BYTE[mapSize * mapSize];
	//        for (DWORD y = 0; y < mapSize; ++y)
	//            for (DWORD x = 0; x < mapSize; ++x)
	//                cloneBuffer[mapSize * y + x] = *(buffer + y * pitch + x);
	//
	//        // calculate weight and offset.
	//        float BlurWeights[GAUSS_MAP_SIZE];
	//        int PixelKernel[GAUSS_MAP_SIZE];
	//        int halfGaussMapSize = GAUSS_MAP_SIZE / 2;
	//        for (int i = 0; i < GAUSS_MAP_SIZE; ++i)
	//        {
	//            PixelKernel[i] = i - halfGaussMapSize;
	//            BlurWeights[i] = GaussianDistribution1((float)(i - halfGaussMapSize), blurSize);
	//        }
	//
	//        // blur the map.
	//        for (int y = 0; y < (int)mapSize; ++y)
	//        {
	//            for (int x = 0; x < (int)mapSize; ++x)
	//            {
	//                float value = 0;
	//
	//                for (int oy = 0; oy < GAUSS_MAP_SIZE; oy++)
	//                {
	//                    float weight = BlurWeights[oy];
	//                    int my = y + PixelKernel[oy];
	//                    if (my < 0) 
	//                        my = 0;
	//
	//                    if (my >= (int)mapSize) 
	//                        my = mapSize - 1;
	//
	//                    value += weight * (float)(cloneBuffer[my * mapSize + x]);
	//                }
	//
	//                if (value < 0) 
	//                    value = 0;
	//
	//                if (value > 255.0f) 
	//                    value = 255.0f;
	//
	//                *(buffer + y * pitch + x) = (BYTE)value;
	//            }
	//        }
	//
	//        SAFE_DELETE_ARRAY(cloneBuffer);
	//    }
	//
	//    BOOL CEntityTerrain::GenerateLightMapRayTrace(const char* mapName, DWORD regionX, DWORD regionY, DWORD mapWidth, DWORD mapHeight, D3DXVECTOR3& lightDir)
	//    {
	//        BOOL ret = FALSE;
	//        D3DXVec3Normalize(&lightDir, &lightDir);
	//
	//        std::vector<CSceneNode*> vecModel;
	//        __GenerateShadowCasterList(GetEngine()->GetSceneManager(), vecModel);
	//
	//        BYTE* map = new BYTE[mapWidth * mapHeight];
	//
	//        float start_x = (float)regionX * TERRAIN_REGION_WIDTH_SIZE;
	//        float start_y = (float)regionY * TERRAIN_REGION_HEIGHT_SIZE;
	//
	//        float fx = TERRAIN_REGION_WIDTH_SIZE / (float)mapWidth;
	//        float fy = TERRAIN_REGION_HEIGHT_SIZE / (float)mapHeight;
	//
	//        for (DWORD map_y = 0; map_y < mapHeight; map_y++)
	//        {
	//            for (DWORD map_x = 0; map_x < mapWidth; map_x++)
	//            {
	//                float x = ((float)map_x + 0.5f) * fx + start_x;
	//                float z = ((float)map_y + 0.5f) * fy + start_y;
	//
	//                float y;
	//                GetGroundHeight(y, x, z);
	//                D3DXVECTOR3 pos;
	//                if (__SceneShadowCasterCollideWithRay(pos, x, y + 1.0f, z, lightDir, vecModel) || __CollideWithRay(pos, x, y + 1.0f, z, lightDir))
	//                    map[map_y * mapWidth + map_x] = 64;
	//                else
	//                    map[map_y * mapWidth + map_x] = 255;
	//            }
	//        }
	//
	//        CEntityTerrainRegion* region = (CEntityTerrainRegion*)_vecTerrainRegion[regionY * _width + regionX];
	//        CTexture2D* tex = region->GetTextureMap(mapName, FALSE);
	//        MK_PROCESS_ERROR(tex);
	//
	//        void* pData = NULL;
	//        DWORD pitch = 0;
	//
	//        ret = tex->LockRect((void**)&pData, &pitch, NULL);
	//        MK_PROCESS_ERROR(ret);
	//
	//        for (DWORD y = 0; y < mapHeight; y++)
	//        {
	//            DWORD* pDstBuf = (DWORD*)((unsigned char*)pData + pitch * y);
	//            BYTE* pSrcBuf = map + y * mapWidth;
	//
	//            for (DWORD x = 0; x < mapWidth; x++)
	//            {
	//                BYTE c = *pSrcBuf++;
	//                *pDstBuf++ = MK_COLOR_ARGB(c, 255, 255, 255);
	//            }
	//        }
	//
	//        ret = tex->Unlock();
	//        MK_PROCESS_ERROR(ret);
	//
	//        ret = TRUE;
	//
	//Exit0:
	//        SAFE_DELETE_ARRAY(map);
	//        return ret;
	//    }
	//
	//    BOOL CEntityTerrain::__CollideWithRay(D3DXVECTOR3& pos, float x, float y, float z, D3DXVECTOR3& dir)
	//    {
	//        for (DWORD regionY = 0; regionY < _height; regionY++)
	//        {
	//            for (DWORD regionX = 0; regionX < _width; regionX++)
	//            {
	//                CEntityTerrainRegion* region = (CEntityTerrainRegion*)_vecTerrainRegion[regionY * _width + regionX];
	//                if (region->CollideWithRay(pos, x, y, z, dir))
	//                    return TRUE;
	//            }
	//        }
	//        
	//        return FALSE;
	//    }
}